package it.tukano.jpgi.test;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.geom.Dimension2D;
import java.awt.geom.Rectangle2D;
import java.util.HashMap;
import java.util.Map;
import javax.swing.AbstractButton;
import javax.swing.ButtonGroup;
import javax.swing.JPanel;
import javax.swing.Scrollable;
import org.apache.batik.bridge.BridgeContext;
import org.apache.batik.bridge.BridgeExtension;
import org.apache.batik.bridge.ExternalResourceSecurity;
import org.apache.batik.bridge.ScriptSecurity;
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.dom.svg.SVGOMDocument;
import org.apache.batik.gvt.GVTTreeWalker;
import org.apache.batik.gvt.GraphicsNode;
import org.apache.batik.gvt.event.EventDispatcher;
import org.apache.batik.gvt.text.Mark;
import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.swing.svg.GVTTreeBuilderAdapter;
import org.apache.batik.swing.svg.GVTTreeBuilderEvent;
import org.apache.batik.util.ParsedURL;
import org.w3c.dom.Element;
import org.w3c.dom.svg.SVGAElement;
import org.w3c.dom.svg.SVGDocument;

public class SvgContainer extends JPanel implements Scrollable {
    
    /* default serial version code */
    private static final long serialVersionUID = 0;

    public Dimension getPreferredScrollableViewportSize() {
        return getPreferredSize();
    }

    public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) {
        return 10;
    }

    public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) {
        return 50;
    }

    public boolean getScrollableTracksViewportWidth() {
        return false;
    }

    public boolean getScrollableTracksViewportHeight() {
        return false;
    }
    
    /** Checks if a field is selected or not (should be a button, if not returns false, if it is returns the selected state of the button */
    public boolean getCheckValue(String buttonName) {
        AbstractButton button = getField(buttonName, AbstractButton.class);
        return button == null ? false : button.isSelected();
    }
    
    public ButtonGroup groupButtons(ButtonGroup group, String... names) {
        if(group == null) group = new ButtonGroup();
        for (int i = 0; i < names.length; i++) {
            String string = names[i];
            AbstractButton button = getField(string, AbstractButton.class);
            if(button != null) group.add(button);
        }
        return group;
    }
    
    public ButtonGroup groupButtons(String... names) {
        return groupButtons(null, names);
    }
    
    public SvgContainer createButtonGroup(String... controlNames) {
        groupButtons(null, controlNames);
        return this;
    }
    
    /** Finds and returns the first added component with the given name of some type */
    public <T extends Component> T getField(String componentName, Class<T> type) {
        Component field = getField(componentName);
        if(field != null && type.isAssignableFrom(field.getClass())) return type.cast(field);
        return null;
    }

    /** Finds and returns the first added component with the given name */
    public Component getField(String componentName) {
        for (int i = 0; i < getComponentCount(); i++) {
            Component c = getComponent(i);
            if(componentName.equals(c.getName())) return c;
        }
        return null;
    }

    private class SVGHack extends JSVGCanvas {
        
        /* default serial version code */
        private static final long serialVersionUID = 0;

        @Override
        protected BridgeContext createBridgeContext(SVGOMDocument doc) {
            BridgeContext context = super.createBridgeContext(doc);
            context.setDynamic(true);
            return context;
        }
    
        public BridgeContext getContext() {
            return this.bridgeContext;
        }

        @Override
        protected UserAgent createUserAgent() {
            final UserAgent agent = super.createUserAgent();
            return new UserAgent() {

                public EventDispatcher getEventDispatcher() {
                    return agent.getEventDispatcher();
                }

                public Dimension2D getViewportSize() {
                    return agent.getViewportSize();
                }

                public void displayError(Exception excptn) {
                    agent.displayError(excptn);
                }

                public void displayMessage(String string) {
                    agent.displayMessage(string);
                }

                public void showAlert(String string) {
                    agent.showAlert(string);
                }

                public String showPrompt(String string) {
                    return agent.showPrompt(string);
                }

                public String showPrompt(String string, String string1) {
                    return agent.showPrompt(string, string1);
                }

                public boolean showConfirm(String string) {
                    return agent.showConfirm(string);
                }

                public float getPixelUnitToMillimeter() {
                    return agent.getPixelUnitToMillimeter();
                }

                public float getPixelToMM() {
                    return agent.getPixelToMM();
                }

                public float getMediumFontSize() {
                    return agent.getMediumFontSize();
                }

                public float getLighterFontWeight(float f) {
                    return agent.getLighterFontWeight(f);
                }

                public float getBolderFontWeight(float f) {
                    return agent.getBolderFontWeight(f);
                }

                public String getDefaultFontFamily() {
                    return agent.getDefaultFontFamily();
                }

                public String getLanguages() {
                    return agent.getLanguages();
                }

                public String getUserStyleSheetURI() {
                    return agent.getUserStyleSheetURI();
                }

                public void openLink(SVGAElement svgae) {
                    agent.openLink(svgae);
                }

                public void setSVGCursor(Cursor cursor) {
                    agent.setSVGCursor(cursor);
                }

                public void setTextSelection(Mark mark, Mark mark1) {
                    agent.setTextSelection(mark, mark1);
                }

                public void deselectAll() {
                    agent.deselectAll();
                }

                public String getXMLParserClassName() {
                    return agent.getXMLParserClassName();
                }

                public boolean isXMLParserValidating() {
                    return agent.isXMLParserValidating();
                }

                public AffineTransform getTransform() {
                    return agent.getTransform();
                }

                public void setTransform(AffineTransform at) {
                    agent.setTransform(at);
                }

                public String getMedia() {
                    return agent.getMedia();
                }

                public String getAlternateStyleSheet() {
                    return agent.getAlternateStyleSheet();
                }

                public Point getClientAreaLocationOnScreen() {
                    return agent.getClientAreaLocationOnScreen();
                }

                public boolean hasFeature(String string) {
                    return agent.hasFeature(string);
                }

                public boolean supportExtension(String string) {
                    return agent.supportExtension(string);
                }

                public void registerExtension(BridgeExtension be) {
                    agent.registerExtension(be);
                }

                public void handleElement(Element elmnt, Object o) {
                }

                public ScriptSecurity getScriptSecurity(String string, ParsedURL purl, ParsedURL purl1) {
                    return agent.getScriptSecurity(string, purl, purl1);
                }

                public void checkLoadScript(String string, ParsedURL purl, ParsedURL purl1) throws SecurityException {
                    agent.checkLoadScript(string, purl, purl1);
                }

                public ExternalResourceSecurity getExternalResourceSecurity(ParsedURL purl, ParsedURL purl1) {
                    return agent.getExternalResourceSecurity(purl, purl1);
                }

                public void checkLoadExternalResource(ParsedURL purl, ParsedURL purl1) throws SecurityException {
                    agent.checkLoadExternalResource(purl, purl1);
                }

                public SVGDocument getBrokenLinkDocument(Element elmnt, String string, String string1) {
                    return agent.getBrokenLinkDocument(elmnt, string, string1);
                }
            };
        }
    };
    
    private final SVGHack canvas = new SVGHack();
    private final Map<String, Rectangle2D> bounds = new HashMap<String, Rectangle2D>();
    
    public SvgContainer(Class<?> cp, String uriString) {
        this(cp.getResource(uriString).toString());
    }

    /**
     * Instance initializer
     */
    public SvgContainer(String svgDocumentUri) {
        setLayout(new CustomLayoutManager());
        super.add(canvas, 0);
        canvas.setOpaque(false);
        canvas.setBackground(new Color(0, 0, 0, 0));
        canvas.addGVTTreeBuilderListener(new GVTTreeBuilderAdapter() {

            @Override
            public void gvtBuildCompleted(GVTTreeBuilderEvent e) {
                GVTTreeWalker walker = new GVTTreeWalker(canvas.getCanvasGraphicsNode());
                GraphicsNode node = walker.getCurrentGraphicsNode();
                while(node != null) {
                    Element element = canvas.getContext().getElement(node);
                    if(element != null) {
                        bounds.put(element.getAttribute("id"), node.getBounds());
                    }
                    node = walker.nextGraphicsNode();
                }
                revalidate();
                repaint();
            }
        });
        canvas.setURI(svgDocumentUri);
    }

    @Override
    public Component add(Component comp) {
        return super.add(comp, 0);
    }
    
    public SvgContainer addNamed(String name, Component comp) {
        comp.setName(name);
        add(comp);
        return this;
    }
    
    public SvgContainer append(Component... components) {
        for (int i = 0; i < components.length; i++) {
            Component component = components[i];
            add(component);
        }
        return this;
    }
    
    private class CustomLayoutManager implements LayoutManager {
        private final Map<Component, String> layoutNames = new HashMap<Component, String>();

        public void addLayoutComponent(String name, Component comp) {
            layoutNames.put(comp, name);
        }

        public void removeLayoutComponent(Component comp) {
        }

        public Dimension preferredLayoutSize(Container parent) {
            synchronized (parent.getTreeLock()) {
                int x = 0, y = 0;
                for (Rectangle2D r : bounds.values()) {
                    if(r == null) continue;
                    x = (int) Math.max(x, r.getMaxX());
                    y = (int) Math.max(y, r.getMaxY());
                }
                x += 100;
                y += 100;
                return new Dimension(x, y);
            }
        }

        public Dimension minimumLayoutSize(Container parent) {
            return preferredLayoutSize(parent);
        }

        public void layoutContainer(Container parent) {
            synchronized (parent.getTreeLock()) {
                Component[] components = parent.getComponents();
                for (int i = 0; i < components.length; i++) {
                    Component component = components[i];
                    if(component instanceof JSVGCanvas) {
                        int w = parent.getWidth();
                        int h = parent.getHeight();
                        component.setBounds(0, 0, w, h);
                    } else {
                        String name = component.getName();
                        Rectangle2D r = bounds.get(name);
                        if(r == null && name != null) {
                            r = bounds.get(name.replace(" ", "_"));
                        }
                        if(r == null) {
                            name = layoutNames.get(component);
                            r = bounds.get(name);
                        }
                        if(r != null) {
                            component.setBounds(r.getBounds());
                        }
                    }
                }
            }
        }
    };
}
